package com.example.mytest.annotation3;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import java.lang.reflect.Method;

/**
 * @ClassName: CustomCacheAspect
 * @Description: 缓存注解需要在方法执行之前有返回值，所以没有通过拦截器处理这个注解，而是通过使用切面在执行方法之前对注解进行处理
 * @Author: 230689
 * @Date: 2021/9/8 10:55
 * @Version: v1.0
 */
@Aspect
@Component
public class CustomCacheAspect {
    private static final Logger logger = LoggerFactory.getLogger(CustomCacheAspect.class);


    /**
     * 在方法执行之前对注解进行处理
     *
     * @param pjp
     * @param customCache 注解
     * @return 返回中的值
     */
    @Around("@annotation(CustomCache) && @annotation(customCache)")
    public Object dealProcess(ProceedingJoinPoint pjp, CustomCache customCache) throws Throwable {


        // 获取参数的类型
        Class[] argTypes = ((MethodSignature) pjp.getSignature()).getParameterTypes();


        Method method = pjp.getTarget().getClass().getMethod(pjp.getSignature().getName(), argTypes);

        String keyValue = parseKey(customCache.key(), method, pjp.getArgs());



        //TODO 业务场景会比这个复杂的多，会涉及参数的解析如key可能是#{id}这些，数据查询
        //TODO 简单demo,模拟redis数据
        Object obj = RedisCache.get(keyValue);
        if (obj != null) {
            System.out.println("模拟从缓存中获取数据:"+obj);
            return obj;
        }

        obj = pjp.proceed();

        if (obj != null) {
            RedisCache.set(keyValue, obj);
        }

        System.out.println("模拟从数据库中获取数据:"+obj);
        return obj;
    }



    /**
     * 解析传入的key值
     *
     * @param field
     * @param method
     * @param args
     * @return
     */
    public static final String parseKey(String field, Method method, Object[] args) {
        if (!StringUtils.hasLength(field))
            return null;

        LocalVariableTableParameterNameDiscoverer nd = new LocalVariableTableParameterNameDiscoverer();
        String[] parameterNames = nd.getParameterNames(method);
        ExpressionParser parser = new SpelExpressionParser();
        StandardEvaluationContext context = new StandardEvaluationContext();

        for (int i = 0; i < parameterNames.length; i++) {
            context.setVariable(parameterNames[i], args[i]);
        }

        return parser.parseExpression(field).getValue(context, String.class);
    }
}